// (c) Copyright 2013 Mike Riedel.
// This source is subject to the Microsoft Public License (Ms-PL).
// All other rights reserved.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FreshMeat.MiniHsmTest
{
   [TestClass]
   public class MiniMachineTest
   {
      [TestMethod]
      public void CreateInitializeTest()
      {
         var test = new MockMachine(true);

         Assert.AreEqual("S11", test.CurrentState, "Expected S11 which is default substate of S1 which is default substate of Root.");
         Assert.AreEqual(1, test.EnterS.Step, "Step 1 - enter S.");
         Assert.AreEqual(2, test.DefaultS.Step, "Step 2 - setdefault for S.");
         Assert.AreEqual(3, test.EnterS1.Step, "Step 3 - enter S1.");
         Assert.AreEqual(4, test.DefaultS1.Step, "Step 4 - setdefault for S1.");
         Assert.AreEqual(5, test.EnterS11.Step, "Step 5 - entered S11.");
         Assert.AreEqual(5, test.Step, "Wrong number of steps.");
         Assert.AreEqual(1, test.EnterS.Count, "Should enter S once.");
         Assert.AreEqual(1, test.DefaultS.Count, "Should default S once.");
         Assert.AreEqual(0, test.ExitS.Count, "Should never exit S.");
         Assert.AreEqual(1, test.EnterS1.Count, "Should enter S1 once.");
         Assert.AreEqual(1, test.DefaultS1.Count, "Should default S1 once.");
         Assert.AreEqual(0, test.ExitS1.Count, "Should never exit S1.");
         Assert.AreEqual(1, test.EnterS11.Count, "Should enter S11 once.");
         Assert.AreEqual(0, test.ExitS11.Count, "Should never exit S11.");
         Assert.AreEqual(0, test.EnterS12.Count, "Should never enter S12.");
         Assert.AreEqual(0, test.ExitS12.Count, "Should never exit S12.");
         Assert.AreEqual(0, test.EnterS2.Count, "Should never enter S2.");
         Assert.AreEqual(0, test.DefaultS2.Count, "Should never default S2.");
         Assert.AreEqual(0, test.ExitS2.Count, "Should never exit S2.");
         Assert.AreEqual(0, test.EnterS21.Count, "Should never enter S21.");
         Assert.AreEqual(0, test.ExitS21.Count, "Should never exit S21.");
         Assert.AreEqual(0, test.EnterS22.Count, "Should never enter S21.");
         Assert.AreEqual(0, test.ExitS22.Count, "Should never exit S21.");
      }

      [TestMethod]
      public void TranstionBetweenNodesWithLcaTest()
      {
         var test = new MockMachine(false);

         // Transition from S11 to S21 which are in separate branches with S as the LCA.
         test.Update(MockMachine.Input.GoToS2, true);

         Assert.AreEqual("S21", test.CurrentState, "Expected S21 which is default substate of S2.");
         Assert.AreEqual(1, test.ExitS11.Step, "Step 1 - Exit S11.");
         Assert.AreEqual(2, test.ExitS1.Step, "Step 2 - Exit S1.");
         Assert.AreEqual(3, test.EnterS2.Step, "Step 3 - Enter S2.");
         Assert.AreEqual(4, test.DefaultS2.Step, "Step 4 - Default S2.");
         Assert.AreEqual(5, test.EnterS21.Step, "Step 5 - Enter S21.");
         Assert.AreEqual(5, test.Step, "Wrong number of steps.");
         Assert.AreEqual(0, test.EnterS.Count, "Should never enter S.");
         Assert.AreEqual(0, test.DefaultS.Count, "Should never default S.");
         Assert.AreEqual(0, test.ExitS.Count, "Should never exit S.");
         Assert.AreEqual(0, test.EnterS1.Count, "Should never enter S1.");
         Assert.AreEqual(0, test.DefaultS1.Count, "Should never default S1.");
         Assert.AreEqual(1, test.ExitS1.Count, "Should exit S1 once.");
         Assert.AreEqual(0, test.EnterS11.Count, "Should enter S11 once.");
         Assert.AreEqual(1, test.ExitS11.Count, "Should exit S11 once.");
         Assert.AreEqual(0, test.EnterS12.Count, "Should never enter S12.");
         Assert.AreEqual(0, test.ExitS12.Count, "Should never exit S12.");
         Assert.AreEqual(1, test.EnterS2.Count, "Should enter S2 once.");
         Assert.AreEqual(1, test.DefaultS2.Count, "Should default S2 once.");
         Assert.AreEqual(0, test.ExitS2.Count, "Should never exit S2.");
         Assert.AreEqual(1, test.EnterS21.Count, "Should enter S21 once.");
         Assert.AreEqual(0, test.ExitS21.Count, "Should never exit S21.");
         Assert.AreEqual(0, test.EnterS22.Count, "Should never enter S21.");
         Assert.AreEqual(0, test.ExitS22.Count, "Should never exit S21.");
      }

      [TestMethod]
      public void TransitionToParentNodeTest()
      {
         var test = new MockMachine(false);

         // Transition from S11 to it's parent S1 (which will immediately default down to S11 again).
         test.Update(MockMachine.Input.GoToS1, true);

         Assert.AreEqual("S11", test.CurrentState, "Expected S11 which is default substate of S1.");
         Assert.AreEqual(1, test.ExitS11.Step, "Step 1 - Exit S11.");
         Assert.AreEqual(2, test.ExitS1.Step, "Step 1 - Exit S1.");
         Assert.AreEqual(3, test.EnterS1.Step, "Step 3 - Enter S1.");
         Assert.AreEqual(4, test.DefaultS1.Step, "Step 2 - Default S1.");
         Assert.AreEqual(5, test.EnterS11.Step, "Step 3 - Enter S11.");
         Assert.AreEqual(5, test.Step, "Wrong number of steps.");
         Assert.AreEqual(0, test.EnterS.Count, "Should never enter S.");
         Assert.AreEqual(0, test.DefaultS.Count, "Should never default S.");
         Assert.AreEqual(0, test.ExitS.Count, "Should never exit S.");
         Assert.AreEqual(1, test.EnterS1.Count, "Should enter S1 once.");
         Assert.AreEqual(1, test.DefaultS1.Count, "Should default S1 once.");
         Assert.AreEqual(1, test.ExitS1.Count, "Should exit S1 once.");
         Assert.AreEqual(1, test.EnterS11.Count, "Should enter S11 once.");
         Assert.AreEqual(1, test.ExitS11.Count, "Should exit S11 once.");
         Assert.AreEqual(0, test.EnterS12.Count, "Should never enter S12.");
         Assert.AreEqual(0, test.ExitS12.Count, "Should never exit S12.");
         Assert.AreEqual(0, test.EnterS2.Count, "Should never enter S2.");
         Assert.AreEqual(0, test.DefaultS2.Count, "Should never default S2.");
         Assert.AreEqual(0, test.ExitS2.Count, "Should never exit S2.");
         Assert.AreEqual(0, test.EnterS21.Count, "Should never enter S21.");
         Assert.AreEqual(0, test.ExitS21.Count, "Should never exit S21.");
         Assert.AreEqual(0, test.EnterS22.Count, "Should never enter S21.");
         Assert.AreEqual(0, test.ExitS22.Count, "Should never exit S21.");
      }

      [TestMethod]
      public void SelfTransitionTest()
      {
         var test = new MockMachine(false);

         // Transition from S11 to itself
         test.Update(MockMachine.Input.GoToS11, true);

         Assert.AreEqual("S11", test.CurrentState, "Expected S11.");
         Assert.AreEqual(1, test.ExitS11.Step, "Step 1 - Exit S11.");
         Assert.AreEqual(2, test.EnterS11.Step, "Step 2 - Enter S11.");
         Assert.AreEqual(2, test.Step, "Wrong number of steps.");
         Assert.AreEqual(0, test.EnterS.Count, "Should never enter S.");
         Assert.AreEqual(0, test.DefaultS.Count, "Should never default S.");
         Assert.AreEqual(0, test.ExitS.Count, "Should never exit S.");
         Assert.AreEqual(0, test.EnterS1.Count, "Should never enter S1.");
         Assert.AreEqual(0, test.DefaultS1.Count, "Should never default S1.");
         Assert.AreEqual(0, test.ExitS1.Count, "Should never exit S1.");
         Assert.AreEqual(1, test.EnterS11.Count, "Should enter S11 once.");
         Assert.AreEqual(1, test.ExitS11.Count, "Should exit S11 once.");
         Assert.AreEqual(0, test.EnterS12.Count, "Should never enter S12.");
         Assert.AreEqual(0, test.ExitS12.Count, "Should never exit S12.");
         Assert.AreEqual(0, test.EnterS2.Count, "Should never enter S2.");
         Assert.AreEqual(0, test.DefaultS2.Count, "Should never default S2.");
         Assert.AreEqual(0, test.ExitS2.Count, "Should never exit S2.");
         Assert.AreEqual(0, test.EnterS21.Count, "Should never enter S21.");
         Assert.AreEqual(0, test.ExitS21.Count, "Should never exit S21.");
         Assert.AreEqual(0, test.EnterS22.Count, "Should never enter S21.");
         Assert.AreEqual(0, test.ExitS22.Count, "Should never exit S21.");
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void DefaultTransitionDuringEntryTest()
      {
         // ReSharper disable ObjectCreationAsStatement
         new MockBadMachine(Badness.TransitionDuringEntry);
         // ReSharper restore ObjectCreationAsStatement
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void DefaultTransitionDuringInitialTest()
      {
         // ReSharper disable ObjectCreationAsStatement
         new MockBadMachine(Badness.TransitionDuringInitial);
         // ReSharper restore ObjectCreationAsStatement
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void DefaultTransitionDuringGetParentTest()
      {
         // ReSharper disable ObjectCreationAsStatement
         new MockBadMachine(Badness.TransitionDuringGetParent);
         // ReSharper restore ObjectCreationAsStatement
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void DefaultReturnNullFromGetParentTest()
      {
         // ReSharper disable ObjectCreationAsStatement
         new MockBadMachine(Badness.ReturnNullFromGetParent);
         // ReSharper restore ObjectCreationAsStatement
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void TransitionDuringEntryTest()
      {
         var machine = new MockBadMachine(Badness.None) {Badness = Badness.TransitionDuringEntry};
         machine.Update(MockBadMachine.Input.Test);
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void TransitionDuringExitTest()
      {
         var machine = new MockBadMachine(Badness.None) {Badness = Badness.TransitionDuringExit};
         machine.Update(MockBadMachine.Input.Test);
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void TransitionDuringInitialTest()
      {
         var machine = new MockBadMachine(Badness.None) {Badness = Badness.TransitionDuringInitial};
         machine.Update(MockBadMachine.Input.Test);
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void TransitionDuringGetParentTest()
      {
         var machine = new MockBadMachine(Badness.None) {Badness = Badness.TransitionDuringGetParent};
         machine.Update(MockBadMachine.Input.Test);
      }

      [TestMethod]
      [ExpectedException(typeof(InvalidOperationException))]
      public void ReturnNullFromGetParentTest()
      {
         var machine = new MockBadMachine(Badness.None) {Badness = Badness.ReturnNullFromGetParent};
         machine.Update(MockBadMachine.Input.Test);
      }
   }
}